import time
from typing import Union


def get_current_time_str():
    return time.strftime("%Y-%m-%d-%H_%M_%S", time.localtime(time.time()))


def get_current_timestamp():
    return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()))


def get_dict_str_in_lines(a_dict: dict):
    titles = []
    values = []
    for k, v in a_dict.items():
        # get k str
        str_k = str(k)
        # get v str
        if isinstance(v, float):
            str_v = "%.4f" % v
            pass
        else:
            str_v = str(v)
            pass
        max_len = max(len(str_k), len(str_v))
        titles.append(str_k.ljust(max_len, ' '))
        values.append(str_v.ljust(max_len, ' '))
        pass
    return "  ".join(titles) + "\n" + "  ".join(values)
    pass


def f_check(se1: tuple, se2: tuple) -> Union[tuple, bool]:
    # 这里的 se1 和 se2 都是 (aspect_start_i, aspect_end_i) 能不能理解为，如果两个离得很近，就把他们合并，如果离得比较远，就返回 false
    if se2[0] == se1[1] or se2[0] == se1[1] + 1:
        return se1[0], se2[1]
    elif se2[1] == se1[0] or se2[1] == se1[0] - 1:
        return se2[0], se1[1]
    return False


def f_join(new_se: tuple, se_list: list) -> list:
    # 这里的 t=(aspect_start_i, aspect_end_i)，se_list 是一个 list，其中存放的也是一个个的 (aspect_start_i, aspect_end_i)
    # 这个方法的功能是尝试将 t 合并到 se 中的每一个范围内，并返回合并之后的新的 list
    result_list = []
    is_join = False
    flag = False  # 代表是否进行了合并的操作（这个地方我感觉是有问题的，更新之后的代码我放在了 f_join2() 这个方法里）
    for se_i, s_e in enumerate(se_list):
        check_r = f_check(new_se, s_e)  # check_r 可能是一个合并后的元组，也可能是 false
        if check_r:  # 如果是是一个元组
            new_se = check_r
            is_join = True
            flag = True
            pass
        elif is_join:  # 已经找到了很接近的
            result_list.append(new_se)
            result_list.append(s_e)
            is_join = False
            pass
        else:  # 如果返回的是 false，代表 t 和 se_ 两个并不接近
            result_list.append(s_e)
            pass
        pass
    if not flag or (flag and is_join):  # 此时代表没有进行任何的合并
        result_list.append(new_se)
        pass
    return result_list


# 这里记录一下原版的 f_join 和 f_check
# def f_check(se1, se2):
#     if se2[0] == se1[1] or se2[0] == se1[1] + 1:
#         return (se1[0], se2[1])
#     elif se2[1] == se1[0] or se2[1] == se1[0] - 1:
#         return (se2[0], se1[1])
#     return False
# def f_join(t, se):
#     r = []
#     is_join = False
#     flag = False
#     for i, se_ in enumerate(se):
#         check_r = f_check(t, se_)
#         if check_r:
#             t = check_r
#             is_join = True
#             flag = True
#         elif is_join:
#             r.append(t)
#             r.append(se_)
#             is_join = False
#         else:
#             r.append(se_)
#     if not flag or (flag and is_join):
#         r.append(t)
#     return r


# 作用：根据 token 中 aspect 的 start_i 和 end_i 找到对应原本句子文本的 start_i 和 end_i，并返回字符串
def get_aspect_str_in_original_text(
        batch_i: int,
        a_s: int,
        a_e: int,
        token2id: list
) -> str:
    # 输入的三个参数应该是 batch_i, aspect_start_i, aspect_end_i
    # 第四个参数 sentence_token_range，代表的是原本的 sentence 按照空格分割，每个单词对应的 token 的范围
    r_s, r_e = token2id[a_s], token2id[a_e]
    return '-'.join([str(batch_i), str(r_s), str(r_e)])


# 功能：将 token 的 start end 映射到原本 text 的 start 和 end
# 返回值：一个 str 的 list
def get_opinion_spans(
        batch_i: int,
        spans: list,
        token2id: list
) -> list:
    result = []
    for opinion_s, opinion_e in spans:
        l_i, r_i = token2id[opinion_s], token2id[opinion_e]
        result.append('-'.join([str(batch_i), str(l_i), str(r_i)]))
        pass
    return result


# 输入：
# a: 形如 i-al-ar 的一个字符串
# o: 形如 [i-ol-or] 的一个列表，其中每个元素形如 i-ol-or
# s: 是一个数字，是 0、1、2 当中的一个
# 输出：输出三个数组，这些数组的长度都是 len(o)
# as_result：是一个 list，其中每个元素是 i-al-ar-s，这里的 s 是数字 0、1、2 的其中一个
# pair_result：是一个 list，其中每个元素是 i-al-ar-ol-or
# trip_result：是一个 list，其中每个元素是 i-al-ar-ol-or-s，这里的 s 是数字 0、1、2 的其中一个
def get_as_pair_and_triplets(a: str, o: list, s: int):
    pair_result = []
    trip_result = []
    as_result = []
    for o_i in o:
        # 生成 as_result
        tt = a.split('-')
        tt.append(str(s))
        as_result.append('-'.join(tt))
        # 生成 pair_result
        t = a.split('-')
        t.extend(o_i.split('-')[1:])
        pair_result.append('-'.join(t))
        # 更新 trip_result
        t.append(str(s))
        trip_result.append('-'.join(t))
        pass
    return as_result, pair_result, trip_result


# 功能：从集合的角度计算【准确率、精确率、f1 值】
# 输入：predict_set 和 golden_set 都是 str 构成的 set
def score_set(predict_set: set, golden_set: set):
    correct_num = len(golden_set & predict_set)
    precision = correct_num / len(predict_set) if len(predict_set) > 0 else 0
    recall = correct_num / len(golden_set) if len(golden_set) > 0 else 0
    f1 = 2 * precision * recall / (precision + recall) if (precision + recall) > 0 else 0
    return precision, recall, f1


# 将一个 set 转化成 key - v 形式
def trans_golden_set_dict(a_set: set):
    a_list = [s.split("-") for s in a_set]
    a_dict = dict()
    for v in a_list:
        v_int_list = [int(vv) for vv in v]
        if a_dict.__contains__(v_int_list[0]):
            a_dict[v_int_list[0]].append(v_int_list[1:])
            pass
        else:
            a_dict[v_int_list[0]] = [v_int_list[1:]]
            pass
        pass
    return a_dict
